<template>
  <div id="kg-container" ref="kg-container"></div>
</template>

<script>
import { Loading } from 'element-ui'
import axios from 'axios'
import { createKnowledgeGraph } from './knowledge-graph'
import { entityTypes } from './entityTypes.js'
import EventBus from '../EventBus'
import Bus from '../EventBus/events'
import { NODE_CLASS, relationMap, nodeTypeMap } from './dataType'

export default {
  name: 'KnowledgeGraph',
  data() {
    return {
      // 3d-force-graph 实例
      graph: null,
      graphData: undefined,
      // 备份一份数据，方便后续处理
      nodes: [],
      links: [],
      currentNodes: [],
      currentLinks: [],
      // Loading 数据
      loading: null,
      // EventBus 数据
      radioSelected: 'all',
      selectedNode: undefined,
      checkEntityTypes: entityTypes
    }
  },
  mounted() {
    let loading = Loading.service({fullscreen: true})

    const container = this.$refs['kg-container']
    this.graph = createKnowledgeGraph(container)
    this.initKnowledgeGraph()

    loading.close()
    this.initEvents()
  },
  methods: {
    initEvents() {
      // 监听 radio 变化
      EventBus.$on(Bus.RADIO_CHANGE, ({ radioSelected }) => {
        this.radioSelected = radioSelected
        this.updateGraph()
      })
      // 监听 搜索
      EventBus.$on(Bus.SEARCH_SELECT, ({ node }) => {
        this.selectedNode = node
        console.log(node)
        // Aim at node from outside it
        const distance = 200
        const distRatio = 1 + distance/Math.hypot(node.x, node.y, node.z)

        this.graph.cameraPosition(
          { x: node.x * distRatio, y: node.y * distRatio, z: node.z * distRatio }, // new position
          node, // lookAt ({ x, y, z })
          3000  // ms transition duration
        )
      })
      // 监听 图例 改变
      EventBus.$on(Bus.LEGEND_CHANGE, ({ checkEntityTypes }) => {
        this.checkEntityTypes = checkEntityTypes
        this.updateGraph()
      })
      // 页面缩放事件
      window.addEventListener('resize', () => {
        new Promise((resolve, reject) => {
          let width, height
          if (this.$refs['kg-container']) {
            width = this.$refs['kg-container'].clientWidth
            height = this.$refs['kg-container'].clientHeight
          }
          resolve([width, height])
        }).then(([width, height]) => {
          if (width && height) {
            this.graph.width(width)
            this.graph.height(height)
          }
        })
      })
    },
    initKnowledgeGraph() {
      const graph = this.graph
      graph.backgroundColor('rgba(0, 0, 0, 0)')
        .nodeId('uuid')
        .linkSource('start_id')
        .linkTarget('end_id')
        .nodeRelSize(8)
        .nodeLabel(node => {
          let nodeType = ''
          if (node.type) {
            nodeType = nodeTypeMap[node.type]
          }
          return (
            `
            <div class="node-tooltip">
              <div class="node-name"><span>名称：</span><span>${node.name}<span></div>
              <div class="node-type"><span>类型：</span><span>${nodeType}<span></div>
            </div>
            `
          )
        })
        .nodeColor((node) => {
          // 如果节点有 color 属性，那么就以他的 color 为颜色
          if (node.color) {
            return node.color
          }
          if (node['class'] === NODE_CLASS.PRODUCT) {
            if (node['name'] === '型号1') {
              return '#8d929e'
            }
            return '#109179'
          } else if (node['class'] === NODE_CLASS.ENTERPRISE) {
            return '#2251ff'
          } else if (node['class'] === NODE_CLASS.PRODUCTIVITY) {
            return '#bc9000'
          } else if (node['class'] === NODE_CLASS.CAPABILITY) {
            return '#da522e'
          }
          return '#109179'
        })
        .linkLabel((link) => {
          return (
            `
            <div class="link-tooltip">
              <div class="link-name">${link.relation_label}关系</div>
              <div class="link-data">&emsp;${link.start_label} >> ${link.end_label}</div>
            </div>
            `
          )
        })
        .linkWidth(2)
        .onNodeHover(node => this.$refs['kg-container'].style.cursor = node ? 'pointer' : null)
        .onNodeClick(node => {
          // Aim at node from outside it
          const distance = 200
          const distRatio = 1 + distance/Math.hypot(node.x, node.y, node.z)

          graph.cameraPosition(
            { x: node.x * distRatio, y: node.y * distRatio, z: node.z * distRatio }, // new position
            node, // lookAt ({ x, y, z })
            3000  // ms transition duration
          )
        })
      
      this.getGraphData()
    },
    getGraphData() {
      axios.get(`${process.env.BASE_URL}mock-data/graph.json`)
        .then((res) => {
          const graphData = res.data
          this.graphData = graphData
          this.nodes = graphData.nodes
          this.links = graphData.links
          this.graph.graphData(graphData)
          EventBus.$emit(Bus.GRAPH_CHANGE, { nodes: this.nodes })
        })
        .catch((err) => {
          console.error(err)
        })
    },
    legendNodeFilter(node) {
      const entityTypeMap = {
        // class
        产品: 'PRODUCT',
        企业: 'ENTERPRISE',
        产能: 'PRODUCTIVITY',
        专业能力: 'CAPABILITY',
        // type
        国有企业: 'nationalized_org',
        私营企业: 'private_org',
        境外企业: 'abroad_org',
        // tag
        断点: 'break_point',
        堵点: 'blocking_point',
        禁限运物资: 'prohibited_substances',
        境外唯一供应商: 'abroad_sole_supplier',
        独子线: 'doko_line',
        境外唯一供应商且无替代: 'domestic_unsubstitution'
      }

      // 现根据 tag 进行排除
      if (node.tag) {
        if (!this.checkEntityTypes.includes('断点')) {
          if (node.tag.includes(entityTypeMap['断点'])) {
            return false
          }
        }
        if (!this.checkEntityTypes.includes('堵点')) {
          if (node.tag.includes(entityTypeMap['堵点'])) {
            return false
          }
        }
        if (!this.checkEntityTypes.includes('禁限运物资')) {
          if (node.tag.includes(entityTypeMap['禁限运物资'])) {
            return false
          }
        }
        if (!this.checkEntityTypes.includes('境外唯一供应商')) {
          if (node.tag.includes(entityTypeMap['境外唯一供应商'])) {
            return false
          }
        }
        if (!this.checkEntityTypes.includes('独子线')) {
          if (node.tag.includes(entityTypeMap['独子线'])) {
            return false
          }
        }
        if (!this.checkEntityTypes.includes('境外唯一供应商且无替代')) {
          if (node.tag.includes(entityTypeMap['境外唯一供应商且无替代'])) {
            return false
          }
        }
      }
      // 按照 class 进行处理，其中 企业 做另外处理
      if (this.checkEntityTypes.includes('产品')) {
        if (node['class'] === entityTypeMap['产品']) {
          return true
        }
      }
      if (this.checkEntityTypes.includes('企业')) {
        if (node['class'] === entityTypeMap['企业']) {
          if (!this.checkEntityTypes.includes('国有企业')) {
            if (node['type'] === entityTypeMap['国有企业']) {
              return false
            }
          }
          if (!this.checkEntityTypes.includes('私营企业')) {
            if (node['type'] === entityTypeMap['私营企业']) {
              return false
            }
          }
          if (!this.checkEntityTypes.includes('境外企业')) {
            if (node['type'] === entityTypeMap['境外企业']) {
              return false
            }
          }
          return true
        }
      }
      if (this.checkEntityTypes.includes('产能')) {
        if (node['class'] === entityTypeMap['产能']) {
          return true
        }
      }
      if (this.checkEntityTypes.includes('专业能力')) {
        if (node['class'] === entityTypeMap['专业能力']) {
          return true
        }
      }
      return false
    },
    updateGraph() {
      const { radioSelected, checkEntityTypes } = this
      if (radioSelected === 'all') {
          // 因为是所有，所以直接根据 legend 处理 this.nodes 和 this.links 即可
          const nodes = this.nodes.filter((node) => this.legendNodeFilter(node))
          const nodesId = nodes.map((node) => node.uuid)
          const links = this.links.filter((link) => {
            if (!nodesId.includes(link.start_id) || !nodesId.includes(link.end_id)) {
              return false
            }
            return true
          })
          this.currentNodes = nodes
          this.currentLinks = links
          this.graph.graphData({ nodes, links })
          EventBus.$emit(Bus.GRAPH_CHANGE, { nodes })
      } else if (radioSelected === 'model-1') {
          // 因为也是所有，所以同上
          const nodes = this.nodes.filter((node) => this.legendNodeFilter(node))
          const nodesId = nodes.map((node) => node.uuid)
          const links = this.links.filter((link) => {
            if (!nodesId.includes(link.start_id) || !nodesId.includes(link.end_id)) {
              return false
            }
            return true
          })
          this.currentNodes = nodes
          this.currentLinks = links
          this.graph.graphData({ nodes, links })
          EventBus.$emit(Bus.GRAPH_CHANGE, { nodes: this.currentNodes })
      } else if (radioSelected === 'model-2') {
          // 当前数据下，直接为空
          const nodes = [], links = []
          this.currentNodes = nodes
          this.currentLinks = links
          this.graph.graphData({ nodes, links })
          EventBus.$emit(Bus.GRAPH_CHANGE, { nodes: this.currentNodes })
      } else {
          this.currentNodes = this.nodes
          this.currentLinks = this.links
          this.graph.graphData({ nodes: this.nodes, links: this.links })
          EventBus.$emit(Bus.GRAPH_CHANGE, { nodes: this.currentNodes })
      }
    }
  },
  beforeDestroy() {
    window.removeEventListener('resize', (e) => {
      console.log('resize event listener has been destroy!', e)
    })
  }
}
</script>

<style lang="less">
#kg-container {
  width: 100%;
  height: 100%;
}

.node-tooltip {
  background-color: #fff;
  color: black;
  border-radius: 4px;
  margin: 4px 4px;
  padding: 4px 4px;

  .node-name {
    margin: 4px 4px;
  }

  .node-type {
    margin: 4px 4px;
  }
}

.link-tooltip {
  background-color: #0d245e;
  border: 1px solid #325fd6;
  color: white;
  border-radius: 4px;
  margin: 4px 4px;
  padding: 4px 4px;

  .link-name {
    margin: 4px 4px;
  }

  .link-data {
    margin: 4px 4px;
  }
}
</style>